<
コンテンツ

Xamarin.Forms 開発者向けの Flutter

コンテンツ

このドキュメントは Xamarin.Forms 開発者を対象としています。 既存の知識を応用したいと考えている Flutter を使用してモバイル アプリを構築します。 Xamarin.Forms フレームワークの基本を理解していれば、 このドキュメントを Flutter 開発のスタート地点として使用できます。

Android と iOS の知識とスキルセット Flutter で構築する場合に重要です。 Flutter はネイティブ オペレーティング システム構成に依存しているため、 ネイティブ Xamarin.Forms プロジェクトを構成する方法と同様です。 Flutter Frameworks は、単一の UI を作成する方法にも似ています。 複数のプラットフォームで使用されています。

このドキュメントは、飛び回ってクックブックとして使用できます。 自分のニーズに最も関連する質問を見つけます。

プロジェクトのセットアップ

アプリはどのように起動しますか?

Xamarin.Forms のプラットフォームごとに、 あなたはそれを呼びますLoadApplication方法、 これにより、新しいアプリケーションが作成され、アプリが起動されます。

LoadApplication(new App());

Flutter では、デフォルトのメイン エントリ ポイントは次のとおりです。mainFlutter アプリをロードする場所。

void main() {
  runApp(const MyApp());
}

Xamarin.Forms では、PageMainPageのプロパティApplicationクラス。

public class App : Application
{
    public App()
    {
        MainPage = new ContentPage
        {
            Content = new Label
            {
                Text = "Hello World",
                HorizontalOptions = LayoutOptions.Center,
                VerticalOptions = LayoutOptions.Center
            }
        };
    }
}

Flutter では、アプリケーション自体も含めて「すべてがウィジェット」です。 次の例は、MyApp、簡単なアプリケーションWidget

class MyApp extends StatelessWidget {
  /// This widget is the root of your application.
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Text(
        'Hello World!',
        textDirection: TextDirection.ltr,
      ),
    );
  }
}

ページはどのように作成しますか?

Xamarin.Forms には多くの種類のページがあります。ContentPageが最も一般的です。 Flutter では、ルート ページを保持するアプリケーション ウィジェットを指定します。 を使用できますMaterialAppをサポートするウィジェットマテリアルデザイン、 または、CupertinoAppiOS スタイルのアプリをサポートするウィジェット、 または、より低いレベルを使用することもできますWidgetsApp、 好きなようにカスタマイズできます。

次のコードは、ステートフル ウィジェットであるホーム ページを定義します。 Flutter では、すべてのウィジェットは不変です。 ただし、次の 2 種類のウィジェットがサポートされています。ステートフルステートレス。 ステートレス ウィジェットの例としては、タイトル、アイコン、画像などがあります。

次の例では、MaterialApp、 ルートページをhome財産。

class MyApp extends StatelessWidget {
  /// This widget is the root of your application.
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

ここから、実際の最初のページは別のページになりますWidget、 そこで自分の状態を作成します。

ステートフルウィジェットなどMyHomePage以下の 2 つの部分で構成されます。 最初の部分はそれ自体不変であり、State物体 オブジェクトの状態を保持します。のStateオブジェクトは永続します ウィジェットの寿命。

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

Stateオブジェクトはbuild()ステートフル ウィジェットのメソッド。

ウィジェット ツリーの状態が変化したら、次の呼び出しを行います。setState()、 これにより、UI のその部分のビルドがトリガーされます。 必ず電話してくださいsetState()必要な場合にのみ、 変更されたのはウィジェット ツリーの部分のみです。 または、UI のパフォーマンスが低下する可能性があります。

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // Take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set the appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

Flutter では、UI (ウィジェット ツリーとも呼ばれます) は不変です。 つまり、一度構築すると状態を変更することはできません。 フィールドを変更すると、Stateクラスを作ってから電話するsetState()ウィジェット ツリー全体を再度再構築します。

この UI の生成方法は Xamarin.Forms とは異なります。 しかし、このアプローチには多くの利点があります。

ビュー

Flutter のページまたは要素に相当するものは何ですか?

ContentPageTabbedPageFlyoutPageあらゆるタイプのページです Xamarin.Forms アプリケーションで使用する可能性があります。 これらのページは保持されますElements さまざまなコントロールを表示します。 Xamarin.Forms では、EntryまたButtonの例ですElement

Flutter では、ほとんどすべてがウィジェットです。 あPageと呼ばれるRouteFlutter では、ウィジェットです。 ボタン、プログレスバー、アニメーションコントローラーはすべてウィジェットです。 ルートを構築するときは、ウィジェット ツリーを作成します。

flutterには以下が含まれます材料成分図書館。 これらは、マテリアル デザインのガイドライン。 マテリアル デザインは柔軟なデザイン システムですすべてのプラットフォーム向けに最適化、iOSを含む。

しかし、Flutter は柔軟で表現力が十分にあります あらゆるデザイン言語を実装します。 たとえば、iOS では、クパチーノのウィジェット次のようなインターフェイスを生成しますAppleのiOSデザイン言語。

ウィジェットを更新するにはどうすればよいですか?

Xamarin.Forms では、それぞれPageまたElementステートフルなクラスであり、 プロパティとメソッドがあります。 更新しますElementプロパティを更新することで、 そしてこれはネイティブ コントロールに伝播されます。

flutterでは、Widgetは不変であり、直接更新することはできません プロパティを変更する場合は、代わりにウィジェットの状態を操作する必要があります。

ここから、ステートフル ウィジェットとステートレス ウィジェットの概念が生まれます。 あStatelessWidgetそれはまさにそのように聞こえます— 状態情報を持たないウィジェット。

StatelessWidgetsユーザーインターフェイスの一部である場合に便利です あなたが説明しているのは何にも依存していません オブジェクト内の構成情報以外。

たとえば、Xamarin.Forms では、これは同様です を配置するImageあなたのロゴと一緒に。 実行中にロゴが変わることはありませんが、 だから、を使用してくださいStatelessWidget flutterで。

受信したデータに基づいて動的にUIを変更したい場合 HTTP 呼び出しまたはユーザー操作を行った後、 それならあなたは協力しなければなりませんStatefulWidgetそして、Flutter フレームワークに次のように伝えます。 ウィジェットのState更新されました、 そのウィジェットを更新できるようになります。

ここで注意すべき重要なことは核心です ステートレス ウィジェットとステートフル ウィジェットはどちらも同じように動作します。 すべてのフレームを再構築しますが、違いは次のとおりです。 のStatefulWidgetがありますState物体 フレーム全体で状態データを保存し、それを復元します。

迷った場合は、次のルールを常に覚えておいてください: ウィジェットが変更された場合 (ユーザーの操作などのため) ステートフルです。 ただし、ウィジェットが変更に反応する場合、それを含む親ウィジェットは、 それ自体が変化に反応しない場合は、ステートレスのままです。

次の例は、StatelessWidget。 共通のStatelessWidgetそれはTextウィジェット。 の実装を見てみると、Textウィジェット サブクラスが見つかりますStatelessWidget

const Text(
  'I like Flutter!',
  style: TextStyle(fontWeight: FontWeight.bold),
);

ご覧のとおり、Textウィジェットには状態情報が関連付けられていません。 コンストラクターで渡されたものだけをレンダリングします。

しかし、「I Like Flutter」を動的に変化させたい場合はどうすればよいでしょうか。 たとえば、FloatingActionButton?

これを実現するには、TextのウィジェットStatefulWidgetユーザーがボタンをクリックすると更新されます。 次の例に示すように:

import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  /// This widget is the root of your application.
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Sample App',
      home: SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  /// Default placeholder text
  String textToShow = 'I Like Flutter';

  void _updateText() {
    setState(() {
      // Update the text
      textToShow = 'Flutter is Awesome!';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sample App')),
      body: Center(child: Text(textToShow)),
      floatingActionButton: FloatingActionButton(
        onPressed: _updateText,
        tooltip: 'Update Text',
        child: const Icon(Icons.update),
      ),
    );
  }
}

ウィジェットをレイアウトするにはどうすればよいですか? XAML ファイルに相当するものは何ですか?

Xamarin.Forms では、ほとんどの開発者が XAML でレイアウトを作成します。 ただし、C# の場合もあります。 Flutter では、コード内でウィジェット ツリーを使用してレイアウトを作成します。

次の例は、パディング付きの単純なウィジェットを表示する方法を示しています。

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: const Text('Sample App')),
    body: Center(
      child: ElevatedButton(
        style: ElevatedButton.styleFrom(
          padding: const EdgeInsets.only(left: 20, right: 30),
        ),
        onPressed: () {},
        child: const Text('Hello'),
      ),
    ),
  );
}

Flutter が提供するレイアウトは、ウィジェットカタログ

レイアウトに要素を追加または削除するにはどうすればよいですか?

Xamarin.Forms では、Elementコードで。 これには、Content財産または呼び出しAdd()またRemove()それがリストだったら。

Flutter では、ウィジェットは不変であるため、直接同等のものはありません。 代わりに、ウィジェットを返す関数を親に渡すことができます。 そしてその子の作成をブールフラグで制御します。

次の例は、2 つのウィジェットを切り替える方法を示しています。 ユーザーがクリックすると、FloatingActionButton:

class SampleApp extends StatelessWidget {
  /// This widget is the root of your application.
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Sample App',
      home: SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  /// Default value for toggle
  bool toggle = true;
  void _toggle() {
    setState(() {
      toggle = !toggle;
    });
  }

  Widget _getToggleChild() {
    if (toggle) {
      return const Text('Toggle One');
    }
    return CupertinoButton(
      onPressed: () {},
      child: const Text('Toggle Two'),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sample App')),
      body: Center(child: _getToggleChild()),
      floatingActionButton: FloatingActionButton(
        onPressed: _toggle,
        tooltip: 'Update Text',
        child: const Icon(Icons.update),
      ),
    );
  }
}

ウィジェットをアニメーション化するにはどうすればよいですか?

Xamarin.Forms では、ViewExtensions を使用して単純なアニメーションを作成します。 などの方法が含まれますFadeToTranslateTo。 これらのメソッドをビューで使用します 必要なアニメーションを実行します。

<Image Source="{Binding MyImage}" x:Name="myImage" />

次に、コードビハインドまたは動作で、これは画像をフェードインします。 1 秒間にわたって。

myImage.FadeTo(0, 1000);

Flutter では、アニメーション ライブラリを使用してウィジェットをアニメーション化します。 ウィジェットをアニメーション化されたウィジェット内にラップすることによって。 を使用してくださいAnimationController、これはAnimation<double>アニメーションを一時停止、シーク、停止、反転できます。 それには、Tickerこれは、vsync が発生したときに通知します。 0 と 1 の間の線形補間を生成します。 実行中の各フレームで。 次に、1 つ以上を作成しますAnimations をコントローラーに取り付けます。

たとえば、次のように使用できます。CurvedAnimation補間された曲線に沿ってアニメーションを実装します。 この意味で、コントローラーはアニメーションの進行の「マスター」ソースです。 そしてそのCurvedAnimation曲線を計算します これは、コントローラーのデフォルトの直線運動を置き換えます。 ウィジェットと同様に、Flutter のアニメーションは合成で動作します。

ウィジェット ツリーを構築するときに、Animationウィジェットのアニメーション化されたプロパティに、 の不透明度などFadeTransition、 そしてコントローラーにアニメーションを開始するように指示します。

次の例は、FadeTransitionそれは色褪せる を押すとウィジェットがロゴに変わりますFloatingActionButton:

import 'package:flutter/material.dart';

void main() {
  runApp(const FadeAppTest());
}

class FadeAppTest extends StatelessWidget {
  /// This widget is the root of your application.
  const FadeAppTest({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Fade Demo',
      home: MyFadeTest(title: 'Fade Demo'),
    );
  }
}

class MyFadeTest extends StatefulWidget {
  const MyFadeTest({super.key, required this.title});

  final String title;

  @override
  State<MyFadeTest> createState() => _MyFadeTest();
}

class _MyFadeTest extends State<MyFadeTest> with TickerProviderStateMixin {
  late final AnimationController controller;
  late final CurvedAnimation curve;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      duration: const Duration(milliseconds: 2000),
      vsync: this,
    );
    curve = CurvedAnimation(
      parent: controller,
      curve: Curves.easeIn,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: Center(
        child: FadeTransition(
          opacity: curve,
          child: const FlutterLogo(size: 100),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          controller.forward();
        },
        tooltip: 'Fade',
        child: const Icon(Icons.brush),
      ),
    );
  }
}

詳細については、を参照してください。アニメーションとモーションのウィジェット、 のアニメーションのチュートリアル、 そしてそのアニメーションの概要

画面上に描画/ペイントするにはどうすればよいですか?

Xamarin.Forms には、画面上に直接描画するための方法が組み込まれていませんでした。 カスタム画像を描画する必要がある場合、多くの人は SkiaSharp を使用するでしょう。 Flutter では、Skia Canvas に直接アクセスできます。 画面上に簡単に描画できます。

Flutter には、キャンバスへの描画に役立つ 2 つのクラスがあります。CustomPaintCustomPainter後者は描画するアルゴリズムを実装します。 キャンバス。

Flutter で署名ペインタを実装する方法を学ぶには、 コリンの答えを参照してくださいカスタムペイント。

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(home: DemoApp()));
}

class DemoApp extends StatelessWidget {
  const DemoApp({super.key});

  @override
  Widget build(BuildContext context) => const Scaffold(body: Signature());
}

class Signature extends StatefulWidget {
  const Signature({super.key});

  @override
  SignatureState createState() => SignatureState();
}

class SignatureState extends State<Signature> {
  List<Offset?> _points = <Offset?>[];

  void _onPanUpdate(DragUpdateDetails details) {
    setState(() {
      final RenderBox referenceBox = context.findRenderObject() as RenderBox;
      final Offset localPosition = referenceBox.globalToLocal(
        details.globalPosition,
      );
      _points = List.from(_points)..add(localPosition);
    });
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onPanUpdate: _onPanUpdate,
      onPanEnd: (details) => _points.add(null),
      child: CustomPaint(
        painter: SignaturePainter(_points),
        size: Size.infinite,
      ),
    );
  }
}

class SignaturePainter extends CustomPainter {
  const SignaturePainter(this.points);

  final List<Offset?> points;

  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = Colors.black
      ..strokeCap = StrokeCap.round
      ..strokeWidth = 5;
    for (int i = 0; i < points.length - 1; i++) {
      if (points[i] != null && points[i + 1] != null) {
        canvas.drawLine(points[i]!, points[i + 1]!, paint);
      }
    }
  }

  @override
  bool shouldRepaint(SignaturePainter oldDelegate) =>
      oldDelegate.points != points;
}

ウィジェットの不透明度はどこにありますか?

Xamarin.Forms では、すべてVisualElementには不透明度があります。 Flutter では、ウィジェットをラップする必要があります。Opacityウィジェットこれを達成するために。

カスタム ウィジェットを作成するにはどうすればよいですか?

Xamarin.Forms では、通常、サブクラス化します。VisualElement、 または既存のものを使用するVisualElement、オーバーライドして、 望ましい動作を実現するメソッドを実装します。

Flutter では、次のようにカスタム ウィジェットを構築します。作曲する(ウィジェットを拡張する代わりに) より小さなウィジェットを作成します。 カスタム コントロールの実装に似ています。 に基づいてGridたくさんのVisualElementが追加されました、 カスタムロジックで拡張しながら。

たとえば、どうやって構築しますかCustomButtonそれはコンストラクターでラベルを受け取りますか? を構成する CustomButton を作成します。ElevatedButton拡張するのではなく、ラベルを使用してElevatedButton:

class CustomButton extends StatelessWidget {
  const CustomButton(this.label, {super.key});

  final String label;

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {},
      child: Text(label),
    );
  }
}

次に、使用しますCustomButton他の Flutter ウィジェットを使用するのと同じように、

@override
Widget build(BuildContext context) {
  return const Center(
    child: CustomButton('Hello'),
  );
}

ページ間を移動するにはどうすればよいですか?

Xamarin.Forms では、NavigationPageクラス 階層的なナビゲーション体験を提供します ユーザーがページ間を移動できる場所、 前にも後ろにも。

Flutter にも同様の実装があります。 を使ってNavigatorRoutes。 あRouteの抽象化ですPageアプリの、 そしてNavigatorですウィジェットルートを管理するものです。

ルートはおおよそ次のようにマッピングされます。Page。 ナビゲーターは Xamarin.Forms と同様の方法で動作します。NavigationPage、 それができるという点でpush()pop()ルートに応じて ビューに移動するか、ビューから戻るか。

ページ間を移動するには、いくつかのオプションがあります。

  • を指定してくださいMap路線名のこと。 (MaterialApp)
  • ルートに直接移動します。 (WidgetsApp)

次の例では、Map

void main() {
  runApp(
    MaterialApp(
      home: const MyAppHome(), // becomes the route named '/'
      routes: <String, WidgetBuilder>{
        '/a': (context) => const MyPage(title: 'page A'),
        '/b': (context) => const MyPage(title: 'page B'),
        '/c': (context) => const MyPage(title: 'page C'),
      },
    ),
  );
}

ルート名をプッシュしてルートに移動します。Navigator

Navigator.of(context).pushNamed('/b');

Navigatorアプリのルートを管理するスタックです。 ルートをスタックにプッシュすると、そのルートに移動します。 スタックからルートをポップすると、前のルートに戻ります。 これは、Futureによって返されましたpush()

async/await.NET 実装と非常によく似ています でさらに詳しく説明されています非同期UI

たとえば、locationルート ユーザーが自分の場所を選択できるようにするため、 次のようにすることもできます。

Object? coordinates = await Navigator.of(context).pushNamed('/location');

そして、「場所」ルート内で、ユーザーが自分の場所を選択すると、 場所を指定して、結果を含むスタックをポップします。

Navigator.of(context).pop({'lat': 43.821757, 'long': -79.226392});

別のアプリに移動するにはどうすればよいですか?

Xamarin.Forms でユーザーを別のアプリケーションに送信するには、 特定の URI スキームを使用する場合は、Device.OpenUrl("mailto://")

Flutter でこの機能を実装するには、 ネイティブ プラットフォーム統合を作成するか、既存のプラグイン、 そのようなurl_launcher、他の多くのパッケージとともに利用可能パブ.dev。

非同期UI

Flutter の Device.BeginOnMainThread() に相当するものは何ですか?

Dart にはシングルスレッド実行モデルがあり、 のサポート付きIsolates (別のスレッドで Dart コードを実行する方法)、 イベントループと非同期プログラミング。 あなたがスポーンしない限り、7a864b9f​​-ecea-46e3-bc77-ec657bb41d2c、 Dart コードはメイン UI スレッドで実行されます イベントループによって駆動されます。

Dart のシングルスレッド モデルは、すべてを実行する必要があるという意味ではありません UI をフリーズさせるブロック操作として。 Xamarin.Forms と同様に、UI スレッドを解放しておく必要があります。 あなたなら使うでしょうasync/awaitタスクを実行するために、 応答を待つ必要があります。

Flutter では、Dart 言語が提供する非同期機能を使用します。 とも呼ばれますasync/await、非同期作業を実行します。 これは C# に非常に似ており、非常に使いやすいはずです。 すべての Xamarin.Forms 開発者向け。

たとえば、UI をハングさせることなくネットワーク コードを実行できます。 使用してasync/awaitそして、Dart に面倒な作業を任せます。

Future<void> loadData() async {
  final Uri dataURL = Uri.parse(
    'https://jsonplaceholder.typicode.com/posts',
  );
  final http.Response response = await http.get(dataURL);
  setState(() {
    data = jsonDecode(response.body);
  });
}

待機されていたネットワーク呼び出しが完了すると、 呼び出して UI を更新するsetState()、 これにより、ウィジェット サブツリーの再構築がトリガーされ、データが更新されます。

次の例では、データを非同期的にロードします。 そしてそれをListView:

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Sample App',
      home: SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List<Map<String, dynamic>> data = <Map<String, dynamic>>[];

  @override
  void initState() {
    super.initState();
    loadData();
  }

  Future<void> loadData() async {
    final Uri dataURL = Uri.parse(
      'https://jsonplaceholder.typicode.com/posts',
    );
    final http.Response response = await http.get(dataURL);
    setState(() {
      data = jsonDecode(response.body);
    });
  }

  Widget getRow(int index) {
    return Padding(
      padding: const EdgeInsets.all(10),
      child: Text('Row ${data[index]['title']}'),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sample App')),
      body: ListView.builder(
        itemCount: data.length,
        itemBuilder: (context, index) {
          return getRow(index);
        },
      ),
    );
  }
}

詳細については、次のセクションを参照してください バックグラウンドで作業を行う場合、 Flutter と Android の違いについて説明します。

作業をバックグラウンド スレッドに移動するにはどうすればよいでしょうか?

Flutterはシングルスレッドでイベントループを実行するため、 スレッド管理を気にする必要はありません またはバックグラウンド スレッドを生成します。 これは Xamarin.Forms に非常に似ています。 ディスク アクセスやネットワーク呼び出しなど、I/O バウンドの作業を行っている場合は、 そうすれば安全に使用できますasync/awaitこれで準備は完了です。

一方、計算集約的な作業を行う必要がある場合は、 CPU をビジー状態にし続けるため、 それをに移動したいのですが、Isolateイベントループのブロックを避けるために、 あなたが保つようにどれでもメインスレッドから外れた一種の作業。 これは、物を別の場所に移動する場合と似ています。 スレッド経由Task.Run()Xamarin.Forms で。

I/O バウンドの作業の場合は、関数を次のように宣言します。async関数、 とawait関数内の長時間実行タスクの場合:

Future<void> loadData() async {
  final Uri dataURL = Uri.parse(
    'https://jsonplaceholder.typicode.com/posts',
  );
  final http.Response response = await http.get(dataURL);
  setState(() {
    data = jsonDecode(response.body);
  });
}

これは通常、ネットワークまたはデータベース呼び出しを行う方法です。 どちらも I/O 操作です。

ただし、処理している場合があります。 大量のデータがあり、UI がハングします。 Flutterでは、使用しますIsolate複数の CPU コアを活用するため 長時間実行されるタスクや計算負荷の高いタスクを実行するため。

アイソレートは別個の実行スレッドです。 メイン実行メモリ ヒープとメモリを共有しません。 これは次の違いですTask.Run()。 これは、メインスレッドから変数にアクセスできないことを意味します。 または呼び出して UI を更新しますsetState()

次の例は、単純な分離で次のことを示しています。 UI を更新するためにメインスレッドにデータを共有する方法。

Future<void> loadData() async {
  final ReceivePort receivePort = ReceivePort();
  await Isolate.spawn(dataLoader, receivePort.sendPort);

  // The 'echo' isolate sends its SendPort as the first message
  final SendPort sendPort = await receivePort.first as SendPort;
  final List<Map<String, dynamic>> msg = await sendReceive(
    sendPort,
    'https://jsonplaceholder.typicode.com/posts',
  );
  setState(() {
    data = msg;
  });
}

// The entry point for the isolate
static Future<void> dataLoader(SendPort sendPort) async {
  // Open the ReceivePort for incoming messages.
  final ReceivePort port = ReceivePort();

  // Notify any other isolates what port this isolate listens to.
  sendPort.send(port.sendPort);
  await for (final dynamic msg in port) {
    final String url = msg[0] as String;
    final SendPort replyTo = msg[1] as SendPort;

    final Uri dataURL = Uri.parse(url);
    final http.Response response = await http.get(dataURL);
    // Lots of JSON to parse
    replyTo.send(jsonDecode(response.body) as List<Map<String, dynamic>>);
  }
}

Future<List<Map<String, dynamic>>> sendReceive(SendPort port, String msg) {
  final ReceivePort response = ReceivePort();
  port.send(<dynamic>[msg, response.sendPort]);
  return response.first as Future<List<Map<String, dynamic>>>;
}

ここ、dataLoader()それはIsolateそれが流れ込む 独自の別個の実行スレッド。 分離では、より多くの CPU を使用する実行が可能になります 処理 (大きな JSON の解析など)、 または、計算量の多い数学を実行する場合、 暗号化や信号処理など。

以下の完全な例を実行できます。

import 'dart:async';
import 'dart:convert';
import 'dart:isolate';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Sample App',
      home: SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List<Map<String, dynamic>> data = <Map<String, dynamic>>[];

  @override
  void initState() {
    super.initState();
    loadData();
  }

  bool get showLoadingDialog => data.isEmpty;

  Future<void> loadData() async {
    final ReceivePort receivePort = ReceivePort();
    await Isolate.spawn(dataLoader, receivePort.sendPort);

    // The 'echo' isolate sends its SendPort as the first message
    final SendPort sendPort = await receivePort.first as SendPort;
    final List<Map<String, dynamic>> msg = await sendReceive(
      sendPort,
      'https://jsonplaceholder.typicode.com/posts',
    );
    setState(() {
      data = msg;
    });
  }

  // The entry point for the isolate
  static Future<void> dataLoader(SendPort sendPort) async {
    // Open the ReceivePort for incoming messages.
    final ReceivePort port = ReceivePort();

    // Notify any other isolates what port this isolate listens to.
    sendPort.send(port.sendPort);
    await for (final dynamic msg in port) {
      final String url = msg[0] as String;
      final SendPort replyTo = msg[1] as SendPort;

      final Uri dataURL = Uri.parse(url);
      final http.Response response = await http.get(dataURL);
      // Lots of JSON to parse
      replyTo.send(jsonDecode(response.body) as List<Map<String, dynamic>>);
    }
  }

  Future<List<Map<String, dynamic>>> sendReceive(SendPort port, String msg) {
    final ReceivePort response = ReceivePort();
    port.send(<dynamic>[msg, response.sendPort]);
    return response.first as Future<List<Map<String, dynamic>>>;
  }

  Widget getBody() {
    if (showLoadingDialog) {
      return getProgressDialog();
    }
    return getListView();
  }

  Widget getProgressDialog() {
    return const Center(child: CircularProgressIndicator());
  }

  ListView getListView() {
    return ListView.builder(
      itemCount: data.length,
      itemBuilder: (context, index) {
        return getRow(index);
      },
    );
  }

  Widget getRow(int index) {
    return Padding(
      padding: const EdgeInsets.all(10),
      child: Text('Row ${data[index]['title']}'),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sample App')),
      body: getBody(),
    );
  }
}

ネットワークリクエストを行うにはどうすればよいですか?

Xamarin.Forms では使用しますHttpClient。 Flutter でネットワーク呼び出しを行うのは簡単です 人気のものを使うときはhttpパッケージ。 これにより、ネットワークの多くが抽象化されます。 通常は自分で実装するかもしれませんが、 ネットワーク通話が簡単になります。

を使用するには、httpパッケージにある場合は、それを依存関係に追加しますpubspec.yaml:

dependencies:
  http: ^1.0.0

ネットワークリクエストを行うには、 電話awaitasync関数http.get():

Future<void> loadData() async {
  final Uri dataURL = Uri.parse(
    'https://jsonplaceholder.typicode.com/posts',
  );
  final http.Response response = await http.get(dataURL);
  setState(() {
    data = jsonDecode(response.body);
  });
}

長時間実行されるタスクの進行状況を表示するにはどうすればよいですか?

Xamarin.Forms では通常、読み込みインジケーターを作成します。 XAML で直接、または AcrDialogs などのサードパーティのプラグインを介して。

Flutter では、ProgressIndicatorウィジェット。 プログラムで制御して進行状況を表示する ブール値フラグを介してレンダリングされるとき。 長時間実行されるタスクが開始される前に状態を更新するように Flutter に指示します。 そして終了後は非表示にします。

以下の例では、ビルド関数が 3 つの異なる関数に分かれています。 機能。もしもshowLoadingDialogtrue(いつwidgets.length == 0)、レンダリングします。ProgressIndicator。 それ以外の場合は、レンダリングListViewネットワーク呼び出しから返されたデータを使用します。

import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Sample App',
      home: SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List<Map<String, dynamic>> data = <Map<String, dynamic>>[];

  @override
  void initState() {
    super.initState();
    loadData();
  }

  bool get showLoadingDialog => data.isEmpty;

  Future<void> loadData() async {
    final Uri dataURL = Uri.parse(
      'https://jsonplaceholder.typicode.com/posts',
    );
    final http.Response response = await http.get(dataURL);
    setState(() {
      data = jsonDecode(response.body);
    });
  }

  Widget getBody() {
    if (showLoadingDialog) {
      return getProgressDialog();
    }
    return getListView();
  }

  Widget getProgressDialog() {
    return const Center(child: CircularProgressIndicator());
  }

  ListView getListView() {
    return ListView.builder(
      itemCount: data.length,
      itemBuilder: (context, index) {
        return getRow(index);
      },
    );
  }

  Widget getRow(int index) {
    return Padding(
      padding: const EdgeInsets.all(10),
      child: Text('Row ${data[index]['title']}'),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sample App')),
      body: getBody(),
    );
  }
}

プロジェクトの構造とリソース

画像ファイルはどこに保存すればよいですか?

Xamarin.Forms にはプラットフォームに依存しない画像の保存方法がありません。 iOS に画像を配置する必要がありましたxcassetフォルダ、 または Android のさまざまなdrawableフォルダー。

Android と iOS はリソースとアセットを別個のアイテムとして扱いますが、 Flutter アプリにはアセットのみがあります。 に存在するすべてのリソースResources/drawable-*Android 上のフォルダー、 Flutter のアセットフォルダーに配置されます。

Flutter は、iOS のような単純な密度ベースの形式に従います。 資産は次のとおりである可能性があります1.0x2.0x3.0x、またはその他の乗数。 flutterにはありませんdp論理ピクセルはありますが、 これは基本的にデバイスに依存しないピクセルと同じです。 flutterズdevicePixelRatio比率を表します 単一の論理ピクセル内の物理ピクセルの数。

Android の密度バケットに相当するものは次のとおりです。

Android 密度修飾子 flutterピクセル率
ldpi 0.75x
mdpi 1.0x
hdpi 1.5x
xhdpi 2.0x
xxhdpi 3.0x
xxxhdpi 4.0x

アセットは任意のフォルダーにあります。 Flutter には事前定義されたフォルダー構造はありません。 資産を(場所とともに)申告します。 の中にpubspec.yamlファイルを開くと、Flutter がそれらを取得します。

という新しい画像アセットを追加するには、my_icon.pngFlutter プロジェクトに、 たとえば、フォルダーに保存する必要があると判断した場合、 勝手に呼ばれるimages、ベース画像 (1.0x) を配置します。 の中にimagesフォルダー、およびサブフォルダー内の他のすべてのバリアント 適切な比率乗数を使用して呼び出されます。

images/my_icon.png       // Base: 1.0x image
images/2.0x/my_icon.png  // 2.0x image
images/3.0x/my_icon.png  // 3.0x image

次に、これらのイメージをpubspec.yamlファイル:

assets:
 - images/my_icon.jpeg

画像に直接アクセスできます。Image.assetウィジェット:

@override
Widget build(BuildContext context) {
  return Image.asset('images/my_icon.png');
}

または使用してAssetImage:

@override
Widget build(BuildContext context) {
  return const Image(
    image: AssetImage('images/my_image.png'),
  );
}

さらに詳しい情報は、アセットと画像の追加

文字列はどこに保存すればよいですか?ローカリゼーションはどのように処理すればよいですか?

.NET とは異なり、resxファイル、 Flutter には現在、文字列を処理するための専用システムがありません。 現時点でのベストプラクティスは、コピーテキストを宣言することです。 クラス内で静的フィールドとして保存し、そこからアクセスします。例えば:

class Strings {
  static const String welcomeMessage = 'Welcome To Flutter';
}

次のようにして文字列にアクセスできます。

Text(Strings.welcomeMessage);

デフォルトでは、Flutter は文字列として米国英語のみをサポートします。 他の言語のサポートを追加する必要がある場合は、 を含むflutter_localizationsパッケージ。 ダーツを追加する必要がある場合もありますintl日付/時刻のフォーマットなど、i10n 機構を使用するためのパッケージ。

dependencies:
  flutter_localizations:
    sdk: flutter
  intl: '^0.17.0'

を使用するには、flutter_localizationsパッケージ、 を指定しますlocalizationsDelegatessupportedLocalesアプリのウィジェットで:

import 'package:flutter_localizations/flutter_localizations.dart';

class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      localizationsDelegates: <LocalizationsDelegate<dynamic>>[
        // Add app-specific localization delegate[s] here
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: <Locale>[
        Locale('en', 'US'), // English
        Locale('he', 'IL'), // Hebrew
        // ... other locales the app supports
      ],
    );
  }
}

デリゲートには実際のローカライズされた値が含まれています。 一方supportedLocalesアプリがサポートするロケールを定義します。 上記の例では、MaterialApp、 したがって、それは両方を持っていますGlobalWidgetsLocalizationsベースウィジェットのローカライズされた値の場合、 そしてMaterialWidgetsLocalizationsマテリアル ウィジェットのローカリゼーション用。 使用する場合WidgetsAppあなたのアプリでは後者は必要ありません。 これら 2 つのデリゲートには「デフォルト」値が含まれていることに注意してください。 ただし、1 人以上の代理人を指定する必要があります 独自のアプリのローカライズ可能なコピーの場合、 それらもローカライズしたい場合。

初期化すると、WidgetsApp(またMaterialApp) を作成しますLocalizationsあなたのためのウィジェット、 あなたが指定した代理人と一緒に。 デバイスの現在のロケールには常にアクセスできます からLocalizations現在のコンテキストからのウィジェット (の形で)Localeオブジェクト)、またはWindow.locale

ローカライズされたリソースにアクセスするには、Localizations.of()方法 特定のデリゲートによって提供される特定のローカリゼーション クラスにアクセスします。 使用intl_translation翻訳可能なコピーを抽出するためのパッケージ にアーブファイルを翻訳してアプリにインポートし直す 一緒に使用するためintl

Flutter の国際化とローカリゼーションの詳細については、 を参照してください国際化ガイド、サンプルコードがあります ありとなしintlパッケージ。

私のプロジェクトファイルはどこにありますか?

Xamarin.Forms には、csprojファイル。 Flutter で最も近いものは pubspec.yaml です。 これには、パッケージの依存関係とさまざまなプロジェクトの詳細が含まれます。 .NET Standard と同様に、 同じディレクトリ内のファイルはプロジェクトの一部とみなされます。

Nuget に相当するものは何ですか?依存関係を追加するにはどうすればよいですか?

.NET エコシステムでは、ネイティブ Xamarin プロジェクトと Xamarin.Forms プロジェクト Nuget と組み込みのパッケージ管理システムにアクセスできました。 Flutter アプリには、ネイティブ Android アプリ、ネイティブ iOS アプリ、および Flutter アプリが含まれています。

Android では、Gradle ビルド スクリプトに追加することで依存関係を追加します。 iOS では、依存関係を追加するには、Podfile

Flutter は、Dart 独自のビルド システムと Pub パッケージ マネージャーを使用します。 このツールは、Android および iOS のネイティブ ラッパー アプリの構築を委任します。 それぞれのビルド システムに適用されます。

一般に、使用しますpubspec.yaml宣言する Flutter で使用する外部依存関係。 Flutter パッケージを見つけるのに適した場所は次のとおりです。パブ.dev。

アプリケーションのライフサイクル

アプリケーションのライフサイクル イベントをリッスンするにはどうすればよいですか?

Xamarin.Forms には、Application含まれているOnStartOnResumeOnSleep。 Flutter では、代わりに同様のライフサイクル イベントをリッスンできます。 に引っ掛けることでWidgetsBinding観察者と聞く者 のdidChangeAppLifecycleState()変更イベント。

監視可能なライフサイクル イベントは次のとおりです。

inactive
アプリケーションは非アクティブ状態にあり、ユーザー入力を受け付けていません。 このイベントはiOSのみです。
paused
アプリケーションは現在ユーザーには表示されませんが、 ユーザー入力に応答せず、バックグラウンドで実行されています。
19d196a1-cd55-4473-8d2b​​-b457e4214d1e
アプリケーションは表示され、ユーザー入力に応答します。
suspending
アプリケーションは一時停止されています。 本イベントはAndroidのみとなります。

これらの状態の意味の詳細については、 を参照してくださいAppLifecycleStatusドキュメンテーション。

レイアウト

StackLayout に相当するものは何ですか?

Xamarin.Forms では、StackLayoutOrientation水平か垂直か。 Flutter にも同様のアプローチがあります。 ただし、使用するのはRowまたColumnウィジェット。

2 つのコード サンプルが同一であることに気付いた場合は、 を除いてRowColumnウィジェット。 子供たちも同じで、この機能は リッチなレイアウトを開発するために活用できます 同じ子供でも残業時間は変わる可能性があります。

@override
Widget build(BuildContext context) {
  return const Row(
    mainAxisAlignment: MainAxisAlignment.center,
    children: <Widget>[
      Text('Row One'),
      Text('Row Two'),
      Text('Row Three'),
      Text('Row Four'),
    ],
  );
}
@override
Widget build(BuildContext context) {
  return const Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: <Widget>[
      Text('Column One'),
      Text('Column Two'),
      Text('Column Three'),
      Text('Column Four'),
    ],
  );

グリッドに相当するものは何ですか?

に最も近い同等のものGridだろうGridView。 これは、Xamarin.Forms で使用しているものよりもはるかに強力です。 あGridView自動スクロールを提供します。 コンテンツが表示可能なスペースを超えています。

@override
Widget build(BuildContext context) {
  return GridView.count(
    // Create a grid with 2 columns. If you change the scrollDirection to
    // horizontal, this would produce 2 rows.
    crossAxisCount: 2,
    // Generate 100 widgets that display their index in the list.
    children: List<Widget>.generate(
      100,
      (index) {
        return Center(
          child: Text(
            'Item $index',
            style: Theme.of(context).textTheme.headlineMedium,
          ),
        );
      },
    ),
  );
}

を使用したことがあるかもしれませんGridXamarin.Forms 内 他のウィジェットをオーバーレイするウィジェットを実装します。 Flutter では、これを次のように実行します。Stackウィジェット。

このサンプルでは、​​互いに重なる 2 つのアイコンを作成します。

@override
Widget build(BuildContext context) {
  return const Stack(
    children: <Widget>[
      Icon(
        Icons.add_box,
        size: 24,
        color: Colors.black,
      ),
      Positioned(
        left: 10,
        child: Icon(
          Icons.add_circle,
          size: 24,
          color: Colors.black,
        ),
      ),
    ],
  );
}

ScrollView に相当するものは何ですか?

Xamarin.Forms では、ScrollViewを包み込むVisualElement、 コンテンツがデバイスの画面より大きい場合は、スクロールします。

Flutter では、最も近い一致はSingleChildScrollViewウィジェット。 スクロール可能にしたいコンテンツをウィジェットに入力するだけです。

@override
Widget build(BuildContext context) {
  return const SingleChildScrollView(
    child: Text('Long Content'),
  );
}

巻物に包みたいものがたくさんある場合は、 たとえ違うものであってもWidget型の場合は、ListView。 これはやりすぎのように思えるかもしれませんが、Flutter ではこれは次のとおりです。 Xamarin.Forms よりもはるかに最適化されており、負荷が低くなります。ListView、 これはプラットフォーム固有のコントロールに戻ります。

@override
Widget build(BuildContext context) {
  return ListView(
    children: const <Widget>[
      Text('Row One'),
      Text('Row Two'),
      Text('Row Three'),
      Text('Row Four'),
    ],
  );
}

Flutter でランドスケープの遷移を処理するにはどうすればよいですか?

風景の遷移は、configChangesAndroidManifest.xml のプロパティ:

<activity android:configChanges="orientation|screenSize" />

ジェスチャー検出とタッチイベント処理

Flutter のウィジェットに GestureRecognizer を追加するにはどうすればよいですか?

Xamarin.Forms では、Elementには、アタッチできるクリック イベントが含まれる場合があります。 多くの要素には、Commandそれが今回のイベントに繋がっています。 あるいは、TapGestureRecognizer。 Flutter には、よく似た 2 つの方法があります。

  1. ウィジェットがイベント検出をサポートしている場合は、ウィジェットに関数を渡して、 関数内で処理します。たとえば、ElevatedButton にはonPressedパラメータ:

    @override
    Widget build(BuildContext context) {
      return ElevatedButton(
        onPressed: () {
          developer.log('click');
        },
        child: const Text('Button'),
      );
    }
  2. ウィジェットがイベント検出をサポートしていない場合は、 のウィジェットGestureDetectorそして関数を渡します にonTapパラメータ。

    class SampleApp extends StatelessWidget {
      const SampleApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: GestureDetector(
              onTap: () {
                developer.log('tap');
              },
              child: const FlutterLogo(size: 200),
            ),
          ),
        );
      }
    }

ウィジェット上で他のジェスチャを処理するにはどうすればよいですか?

Xamarin.Forms では、GestureRecognizerView。 通常は次のように制限されます。TapGestureRecognizerPinchGestureRecognizerPanGestureRecognizerSwipeGestureRecognizerDragGestureRecognizerDropGestureRecognizer自分で構築した場合を除きます。

Flutter では、GestureDetector を使用して、 次のような幅広いジェスチャを聞くことができます。

  • タップ
onTapDown
タップを引き起こす可能性のあるポインター 特定の場所で画面に接触しました。
onTapUp
タップをトリガーするポインター 特定の場所で画面との接触が停止しました。
onTap
タップが発生しました。
onTapCancel
以前にトリガーしたポインターonTapDownタップの原因にはなりません。
  • ダブルタップ
onDoubleTap
ユーザーが画面の同じ場所を 2 回タップしました 立て続けに。
  • 長押し
onLongPress
ポインターが画面に触れたままになっている 長時間同じ場所にいること。
  • 垂直方向のドラッグ
onVerticalDragStart
ポインタが画面に触れて垂直に動き始める可能性があります。
onVerticalDragUpdate
画面に接触しているポインタ さらに垂直方向に移動しました。
onVerticalDragEnd
以前に接触していたポインタ 画面と垂直方向の移動が接触しなくなりました 画面とともに特定の速度で移動していた 画面に接触しなくなったとき。
  • 水平方向のドラッグ
onHorizontalDragStart
ポインタが画面に触れて水平に動き始める可能性があります。
onHorizontalDragUpdate
画面に接触しているポインタ さらに水平方向に移動しました。
onHorizontalDragEnd
以前に接触していたポインタ 画面と水平移動が接触しなくなりました 画面とともに特定の速度で移動していた 画面に接触しなくなったとき。

次の例は、GestureDetectorダブルタップで Flutter ロゴを回転させます。

class RotatingFlutterDetector extends StatefulWidget {
  const RotatingFlutterDetector({super.key});

  @override
  State<RotatingFlutterDetector> createState() =>
      _RotatingFlutterDetectorState();
}

class _RotatingFlutterDetectorState extends State<RotatingFlutterDetector>
    with SingleTickerProviderStateMixin {
  late final AnimationController controller;
  late final CurvedAnimation curve;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      duration: const Duration(milliseconds: 2000),
      vsync: this,
    );
    curve = CurvedAnimation(parent: controller, curve: Curves.easeIn);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: GestureDetector(
          onDoubleTap: () {
            if (controller.isCompleted) {
              controller.reverse();
            } else {
              controller.forward();
            }
          },
          child: RotationTransition(
            turns: curve,
            child: const FlutterLogo(size: 200),
          ),
        ),
      ),
    );
  }
}

リストビューとアダプター

Flutter の ListView に相当するものは何ですか?

と同等のものListViewFlutter では…ListView

Xamarin.Forms 内ListViewを作成すると、ViewCellそしておそらくDataTemplateSelectorそれをに渡しますListView、 これは各行をあなたのものでレンダリングしますDataTemplateSelectorまたViewCell戻り値。 ただし、多くの場合、セルのリサイクルを必ずオンにする必要があります。 そうしないと、メモリの問題が発生し、スクロール速度が遅くなります。

Flutter の不変ウィジェット パターンにより、 ウィジェットのリストをListView、 Flutter はスクロールが高速かつスムーズであることを保証します。

import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  /// This widget is the root of your application.
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Sample App',
      home: SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatelessWidget {
  const SampleAppPage({super.key});

  List<Widget> _getListData() {
    return List<Widget>.generate(
      100,
      (index) => Padding(
        padding: const EdgeInsets.all(10),
        child: Text('Row $index'),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sample App')),
      body: ListView(children: _getListData()),
    );
  }
}

どのリスト項目がクリックされたかを確認するにはどうすればよいですか?

Xamarin.Forms では、ListView にはItemTapped方法 どの項目がクリックされたかを確認します。 他にも使用した可能性のあるテクニックはたくさんあります いつチェックするかなどSelectedItemまたEventToCommand行動が変わります。

Flutter では、渡されたウィジェットによって提供されるタッチ処理を使用します。

import 'dart:developer' as developer;
import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Sample App',
      home: SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List<Widget> _getListData() {
    return List<Widget>.generate(
      100,
      (index) => GestureDetector(
        onTap: () {
          developer.log('Row $index tapped');
        },
        child: Padding(
          padding: const EdgeInsets.all(10),
          child: Text('Row $index'),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sample App')),
      body: ListView(children: _getListData()),
    );
  }
}

ListView を動的に更新するにはどうすればよいですか?

Xamarin.Forms で、ItemsSourceプロパティをObservableCollection、 ViewModel 内のリストを更新するだけです。 あるいは、新しいものを割り当てることもできます。ListItemSource財産。

Flutter では、動作が少し異なります。 内のウィジェットのリストを更新すると、setState()方法、 データが視覚的に変化していないことがすぐにわかります。 なぜなら、いつsetState()と呼ばれます、 Flutter レンダリング エンジンはウィジェット ツリーを確認します。 何かが変わったかどうかを確認します。 それがあなたの手元に届いたら、ListViewを実行します。==チェック、 そして、その2つがListViewも同じです。 何も変更されていないため、更新する必要はありません。

を更新する簡単な方法については、ListView、 新しいを作成しますListの中にsetState()、 古いリストから新しいリストにデータをコピーします。 このアプローチはシンプルですが、大規模なデータセットには推奨されません。 次の例に示すように。

import 'dart:developer' as developer;
import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  /// This widget is the root of your application.
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Sample App',
      home: SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List<Widget> widgets = <Widget>[];

  @override
  void initState() {
    super.initState();
    for (int i = 0; i < 100; i++) {
      widgets.add(getRow(i));
    }
  }

  Widget getRow(int index) {
    return GestureDetector(
      onTap: () {
        setState(() {
          widgets = List<Widget>.from(widgets);
          widgets.add(getRow(widgets.length));
          developer.log('Row $index');
        });
      },
      child: Padding(
        padding: const EdgeInsets.all(10),
        child: Text('Row $index'),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sample App')),
      body: ListView(children: widgets),
    );
  }
}

リストを作成するための推奨される効率的かつ効果的な方法 を使用しますListView.Builder。 この方法は、動的リストがある場合に最適です または非常に大量のデータを含むリスト。 これは本質的に Android の RecyclerView と同等です。 これにより、リスト要素が自動的にリサイクルされます。

import 'dart:developer' as developer;
import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  /// This widget is the root of your application.
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Sample App',
      home: SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List<Widget> widgets = [];

  @override
  void initState() {
    super.initState();
    for (int i = 0; i < 100; i++) {
      widgets.add(getRow(i));
    }
  }

  Widget getRow(int index) {
    return GestureDetector(
      onTap: () {
        setState(() {
          widgets.add(getRow(widgets.length));
          developer.log('Row $index');
        });
      },
      child: Padding(
        padding: const EdgeInsets.all(10),
        child: Text('Row $index'),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sample App')),
      body: ListView.builder(
        itemCount: widgets.length,
        itemBuilder: (context, index) {
          return getRow(index);
        },
      ),
    );
  }
}

を作成する代わりに、ListViewを作成します。ListView.builderこれは 2 つの重要なパラメータを取ります: リストの初期長、 そしてアイテムビルダー機能。

アイテムビルダー関数は次のようなものです。getView関数 Android アダプター内。それは位置をとり、 そして、その位置にレンダリングしたい行を返します。

最後に、しかし最も重要なことですが、onTap()関数 リストを再作成するのではなく、リストに追加します。

詳細については、を参照してください。初めての Flutter アプリコードラボ。

テキストの操作

テキスト ウィジェットにカスタム フォントを設定するにはどうすればよいですか?

Xamarin.Forms では、各ネイティブ プロジェクトにカスタム フォントを追加する必要があります。 次に、あなたの中でElementこのフォント名を割り当てます にFontFamily属性を使用するfilename#fontnameそしてちょうどfontnameiOS用。

Flutterではフォントファイルをフォルダに置いて参照します の中にpubspec.yaml画像をインポートする方法と同様に、ファイルを作成します。

fonts:
  - family: MyCustomFont
    fonts:
      - asset: fonts/MyCustomFont.ttf
      - style: italic

次にフォントをTextウィジェット:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: const Text('Sample App')),
    body: const Center(
      child: Text(
        'This is a custom font text',
        style: TextStyle(fontFamily: 'MyCustomFont'),
      ),
    ),
  );
}

テキスト ウィジェットのスタイルを設定するにはどうすればよいですか?

フォントに加えて、他のスタイル要素もカスタマイズできます。Textウィジェット。 のスタイルパラメータTextウィジェットはTextStyle物体、 ここでは、次のような多くのパラメータをカスタマイズできます。

  • color
  • decoration
  • decorationColor
  • decorationStyle
  • fontFamily
  • fontSize
  • fontStyle
  • fontWeight
  • hashCode
  • height
  • inherit
  • letterSpacing
  • textBaseline
  • wordSpacing

フォーム入力

ユーザー入力を取得するにはどうすればよいですか?

Xamarin.Formselementを直接クエリできるようになります。elementそのプロパティの状態を判断するには、 または、それがプロパティにバインドされているかどうかViewModel

Flutter での情報の取得は特殊なウィジェットによって処理されます そして、あなたが慣れているやり方とは異なります。 持っている場合は、TextFieldまたはTextFormField、 を供給できますTextEditingControllerユーザー入力を取得するには:

import 'package:flutter/material.dart';

class MyForm extends StatefulWidget {
  const MyForm({super.key});

  @override
  State<MyForm> createState() => _MyFormState();
}

class _MyFormState extends State<MyForm> {
  /// Create a text controller and use it to retrieve the current value
  /// of the TextField.
  final TextEditingController myController = TextEditingController();

  @override
  void dispose() {
    // Clean up the controller when disposing of the widget.
    myController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Retrieve Text Input')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: TextField(controller: myController),
      ),
      floatingActionButton: FloatingActionButton(
        // When the user presses the button, show an alert dialog with the
        // text that the user has typed into our text field.
        onPressed: () {
          showDialog(
            context: context,
            builder: (context) {
              return AlertDialog(
                // Retrieve the text that the user has entered using the
                // TextEditingController.
                content: Text(myController.text),
              );
            },
          );
        },
        tooltip: 'Show me the value!',
        child: const Icon(Icons.text_fields),
      ),
    );
  }
}

詳細と完全なコードのリストは、次の場所にあります。テキストフィールドの値を取得する、 から flutterクックブック

エントリのプレースホルダーに相当するものは何ですか?

Xamarin.Forms では、いくつかのElementsをサポートするPlaceholder財産 値を割り当てることができます。例えば:

<Entry Placeholder="This is a hint">

Flutter では、「ヒント」またはプレースホルダー テキストを簡単に表示できます を追加して入力してくださいInputDecoration物体 にdecorationテキスト ウィジェットのコンストラクター パラメーター。

TextField(
  decoration: InputDecoration(hintText: 'This is a hint'),
),

検証エラーを表示するにはどうすればよいですか?

Xamarin.Forms を使用して、視覚的なヒントを提供したい場合は、 検証エラーが発生した場合は、新しいプロパティを作成する必要があります。VisualElementを囲んでいるElement検証エラーがあった。

Flutter では、InputDecoration オブジェクトを介して テキスト ウィジェットの装飾コンストラクター。

ただし、最初からエラーを表示することは望ましくありません。 代わりに、ユーザーが無効なデータを入力した場合、 状態を更新し、新しい値を渡しますInputDecoration物体。

import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  /// This widget is the root of your application.
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Sample App',
      home: SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  String? _errorText;

  String? _getErrorText() {
    return _errorText;
  }

  bool isEmail(String em) {
    const String emailRegexp =
        r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|'
        r'(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|'
        r'(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
    final RegExp regExp = RegExp(emailRegexp);
    return regExp.hasMatch(em);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sample App')),
      body: Center(
        child: TextField(
          onSubmitted: (text) {
            setState(() {
              if (!isEmail(text)) {
                _errorText = 'Error: This is not an email';
              } else {
                _errorText = null;
              }
            });
          },
          decoration: InputDecoration(
            hintText: 'This is a hint',
            errorText: _getErrorText(),
          ),
        ),
      ),
    );
  }
}

flutterプラグイン

ハードウェア、サードパーティのサービス、およびプラットフォームとの対話

プラットフォームおよびプラットフォームのネイティブ コードと対話するにはどうすればよいですか?

Flutter は、基盤となるプラットフォーム上でコードを直接実行しません。 むしろ、Flutter アプリを構成する Dart コードはネイティブに実行されます。 デバイス上で、プラットフォームによって提供される SDK を「回避」します。 つまり、たとえば、Dart でネットワーク リクエストを実行すると、 Dart コンテキストで直接実行されます。 Android または iOS API を使用しない 通常、ネイティブ アプリを作成するときに利用します。 Flutter アプリは引き続きネイティブ アプリでホストされていますViewControllerまたActivity見方としては、 ただし、これやネイティブ フレームワークに直接アクセスすることはできません。

これは、Flutter アプリがこれらのネイティブ API と対話できないという意味ではありません。 または、お持ちのネイティブ コードを使用して実行することもできます。 Flutter が提供するプラットフォームチャネルと通信し、データを交換するViewControllerまたActivityFlutter ビューをホストします。 プラットフォーム チャネルは本質的には非同期メッセージング メカニズムです Dart コードとホストをブリッジします。ViewControllerまたActivityおよびそれが実行される iOS または Android フレームワーク。 プラットフォーム チャネルを使用してネイティブ側でメソッドを実行できます。 たとえば、デバイスのセンサーからデータを取得することもできます。

プラットフォーム チャネルを直接使用することに加えて、 さまざまな既製のものを使用できますプラグイン特定の目的のためにネイティブ コードと Dart コードをカプセル化します。 たとえば、プラグインを使用してアクセスできます。 カメラ ロールとデバイスのカメラを Flutter から直接、 独自の統合を作成する必要はありません。 プラグインは次の場所にありますパブ.dev、 Dart と Flutter のオープンソース パッケージ リポジトリ。 一部のパッケージは iOS でのネイティブ統合をサポートしている場合があります。 または Android、あるいはその両方。

pub.dev でニーズに合ったプラグインが見つからない場合は、 あなたはできる自分で書いてください、 とpub.dev で公開します

GPS センサーにアクセスするにはどうすればよいですか?

使用geolocatorコミュニティプラグイン。

カメラにアクセスするにはどうすればよいですか?

cameraプラグインはカメラにアクセスするためによく使用されます。

Facebook にログインするにはどうすればよいですか?

Facebook でログインするには、flutter_facebook_loginコミュニティプラグイン。

Firebase の機能を使用するにはどうすればよいですか?

Firebase のほとんどの機能は以下でカバーされています。ファーストパーティのプラグイン。 これらのプラグインはファーストパーティの統合であり、Flutter チームによって維持されています。

  • firebase_admobFirebase AdMob 用
  • firebase_analyticsFirebase アナリティクス用
  • firebase_authFirebase認証の場合
  • firebase_databaseFirebase RTDB 用
  • firebase_storageFirebaseクラウドストレージ用
  • firebase_messagingFirebase メッセージング (FCM) 用
  • flutter_firebase_uiFirebase Auth 統合を迅速に行うため (Facebook、Google、Twitter、電子メール)
  • cloud_firestoreFirebase クラウド Firestore 用

pub.dev では、サードパーティの Firebase プラグインもいくつか見つけることができます。 ファーストパーティのプラグインが直接カバーしていない領域をカバーします。

独自のカスタム ネイティブ統合を構築するにはどうすればよいですか?

Flutter が実行するプラットフォーム固有の機能がある場合 またはコミュニティプラグインが見つからない、 次のように独自のものを構築できますパッケージとプラグインの開発ページ。

Flutter のプラグイン アーキテクチャを一言で言うと、 Android でのイベント バスの使用によく似ています。 メッセージを送信し、受信者に結果を処理させて出力させます。 あなたに戻って。この場合、受信側はネイティブ側で実行されるコードです。 Android または iOS で。

テーマ (スタイル)

アプリのテーマを設定するにはどうすればよいですか?

Flutter には、マテリアル デザインの美しい組み込み実装が付属しています。 スタイルとテーマのニーズの多くを処理します 通常はそうするでしょう。

Xamarin.Forms にはグローバルがありますResourceDictionaryアプリ全体でスタイルを共有できます。 あるいは、現在プレビュー中のテーマのサポートもあります。

Flutter では、トップレベルのウィジェットでテーマを宣言します。

アプリでマテリアル コンポーネントを最大限に活用するには、 最上位のウィジェットを宣言できますMaterialAppアプリケーションへのエントリ ポイントとして。MaterialApp便利なウィジェットです 一般に必要な多数のウィジェットをラップします マテリアル デザインを実装するアプリケーション向け。 それは、WidgetsAppマテリアル固有の機能を追加することによって。

を使用することもできますWidgetsAppアプリのウィジェットとして、 同じ機能の一部を提供しますが、 しかし、それほど裕福ではありませんMaterialApp

子コンポーネントの色とスタイルをカスタマイズするには、 渡すThemeDataに反対するMaterialAppウィジェット。 たとえば、次のコードでは、 プライマリ スウォッチは青に設定され、テキスト選択の色は赤に設定されます。

class SampleApp extends StatelessWidget {
  /// This widget is the root of your application.
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        textSelectionTheme:
            const TextSelectionThemeData(selectionColor: Colors.red),
      ),
      home: const SampleAppPage(),
    );
  }
}

データベースとローカルストレージ

共有設定または UserDefaults にアクセスするにはどうすればよいですか?

Xamarin.Forms 開発者はおそらくよく知っているでしょう。Xam.Plugins.Settingsプラグイン。

Flutter では、以下を使用して同等の機能にアクセスします。shared_preferencesプラグイン。このプラグインは 両方の機能UserDefaultsそしてアンドロイド 同等、SharedPreferences

Flutter で SQLite にアクセスするにはどうすればよいですか?

Xamarin.Forms では、ほとんどのアプリケーションはsqlite-net-pclSQLite データベースにアクセスするためのプラグイン。

Flutter では、次のコマンドを使用してこの機能にアクセスします。sqfliteプラグイン。

デバッグ

Flutter でアプリをデバッグするにはどのようなツールを使用できますか?

使用開発ツールFlutter または Dart アプリをデバッグするためのスイート。

DevTools には、プロファイリング、ヒープの検査、 ウィジェット ツリーの検査、診断のログ記録、デバッグ、 実行されたコード行を観察し、 メモリ リークとメモリの断片化をデバッグします。 詳細については、「開発ツールドキュメンテーション。

通知

プッシュ通知を設定するにはどうすればよいですか?

Android では、Firebase Cloud Messaging を使用してセットアップします アプリのプッシュ通知。

Flutter では、次のコマンドを使用してこの機能にアクセスします。firebase_messagingプラグイン。 Firebase Cloud Messaging API の使用方法の詳細については、firebase_messagingプラグインのドキュメント。

9934d419-a53d-46b3-9508-88aa39652料金